﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;

namespace SimpleFramework
{
#if UNITY_EDITOR
    [System.Serializable]
#endif
    public class ResAssetBundle: Reference, IAssetHandle
    {
#if UNITY_EDITOR
        [SerializeField]
#endif
        protected string m_Name;
#if UNITY_EDITOR
        [SerializeField]
#endif
        protected string m_Url;
        protected AssetBundle m_Asset;
        protected bool m_EnableRecycle = true;
        protected TemplateLoader<AssetBundle> m_Loader;

        public string Url { get { return m_Url; } }
        public bool IsLoaded { get { return m_Asset != null; } }
        public TemplateLoader<AssetBundle> Loader { get { return m_Loader; } }

#if UNITY_EDITOR
        [SerializeField]
#endif
        protected State m_State = State.Empty;
        protected string[] m_DependAssetNames;
        protected ResAssetBundle[] m_Dependencies;
        protected int m_LoadCount;
        protected UnityEvent<ResAssetBundle> m_Complete = new UnityEvent<ResAssetBundle>();
        protected UnityEvent<ResAssetBundle, string> m_Failure = new UnityEvent<ResAssetBundle, string>();
        public ResAssetBundle[] Dependencies { get { return m_Dependencies; } }

        protected enum State
        {
            Empty,
            Waiting,
            Loading,
            Loaded,
            Release
        }


        public ResAssetBundle(string name)
        {
            m_Name = name;
            Start();
        }

        private void Start()
        {
            AddRef();
            m_Url = PathSystem.Instance.Search($"bundle/{m_Name}");
            m_DependAssetNames = ResManager.Instance.GetAllDependencies(m_Name);
            m_Dependencies = new ResAssetBundle[m_DependAssetNames.Length];
            m_LoadCount = m_Dependencies.Length;

            if (m_LoadCount > 0)
            {
                for (int i = 0; i < m_DependAssetNames.Length; ++i)
                {
                    m_Dependencies[i] = ResManager.Instance.GetAssetBundleOrCreate(m_DependAssetNames[i]);
                    m_Dependencies[i].AddRef();
                    m_Dependencies[i].ListenLoadComplete(OnLoadDependencieComplete);
                }
            }
            else
            {
                m_State = State.Loading;
                m_Loader = FileSystem.Instance.LoadAssetBundle(m_Url, OnLoadComplete);
            }
        }

        private void OnLoadDependencieComplete(ResAssetBundle assetBundle)
        {
            m_LoadCount--;
            if (m_LoadCount <= 0)
            {
                m_State = State.Loading;
                m_Loader = FileSystem.Instance.LoadAssetBundle(m_Url, OnLoadComplete, OnLoadFailure);
            }
        }

        private void OnLoadComplete(ILoader loader, AssetBundle assetBundle)
        {
            m_State = State.Loaded;
            m_Loader = null;
            m_Asset = assetBundle;
            RemoveRef();
            m_Complete.Invoke(this);
            m_Complete.RemoveAllListeners();
        }

        private void OnLoadFailure(ILoader loader, string error)
        {
            RemoveRef();
            m_State = State.Empty;
            m_Failure.Invoke(this, error);
            m_Failure.RemoveAllListeners();
        }

        public T LoadAsset<T>(string assetName) where T : Object
        {
            return m_Asset.LoadAsset<T>(assetName);
        }

        public Object LoadAsset(string assetName, System.Type type)
        {
            return m_Asset.LoadAsset(assetName, type);
        }

        public AssetBundleRequest LoadAssetAsync<T>(string assetName) where T : Object
        {
            return m_Asset.LoadAssetAsync<T>(assetName);
        }

        public void ListenLoadComplete(UnityAction<ResAssetBundle> onComplete)
        {
            if (m_Asset != null)
            {
                onComplete.Invoke(this);
            }
            else
            {
                m_Complete.AddListener(onComplete);
            }
        }

        public void ListenLoadFailure(UnityAction<ResAssetBundle, string> onFailure)
        {
            if (m_Asset == null)
            {
                m_Failure.AddListener(onFailure);
            }
        }

        /// <summary>
        /// 停止下载
        /// </summary>
        public virtual void Abort()
        {
            if (m_Loader != null)
            {
                m_Loader.Abort();
                m_Loader = null;
            }
        }

        /// <summary>
        /// 销毁
        /// </summary>
        public virtual void Destroy()
        {
            if (m_Loader != null)
            {
                m_Loader.Abort();
                m_Loader = null;
            }

            if (m_Asset != null)
            {
                OnDestroy();
            }

            SetEnableRecycle(true);
        }

        /// <summary>
        /// 销毁
        /// </summary>
        protected virtual void OnDestroy()
        {
            if (m_Dependencies != null)
            {
                foreach (var dependencie in m_Dependencies)
                {
                    dependencie.RemoveRef();
                }
                m_Dependencies = null;
            }

            if (m_Asset != null)
            {
                m_Asset.Unload(true);
                m_Asset = null;
                m_State = State.Release;
                ClearRef();
            }
        }

        /// <summary>
        /// 检测是否可以回收资源
        /// </summary>
        /// <returns></returns>
        public virtual bool CheckRecycle()
        {
            return m_EnableRecycle && m_Asset != null && this.RefCount <= 0 && this.Time > ResSetting.MAX_LIFE_TIME;
        }

        /// <summary>
        /// 设置是否可以回收
        /// </summary>
        /// <param name="enable"></param>
        public virtual void SetEnableRecycle(bool enable)
        {
            m_EnableRecycle = enable;
        }
    }
}